# .data段 包含了已经初始化的数据项的数据定义，已经初始化的数据在程序开始运行前就拥有了自己的值。这些值是可执行文件的一部分
#  当可执行文件被加载到内存中用于执行时，它们被加载到内存中。 定义的初始数据项越多，可执行文件就越大，需要更长的时间从磁盘加载到内存
.code32
.data
    msg: # 变量名 代表变量值的地址
        .ascii "hello" # 变量的值 字符串(h是最低有效元素，小端序存放结果为hello)
        len = . - msg
   snippet:
        .ascii "KANGAROO"
    x:
       .int 0x12345678 # 数字类型 1是最高有效位，小端序存放结果为78 56 34 12(4个字节)

# .bss段,当程序需要从磁盘上读取文件数据时，读取的数据需要一个缓冲区来存放，这样的缓冲区就是在.bss段中定义的。
# .bss段中定义数据项不会添加到可执行文件的大小中。
#.bss

# 真正组成程序的机器指令存放在.text段中,一般情况下,.text段中不进行数据项的定义
.text
    .global _start
        _start:
#1、 mov指令：复制,l:双字 4字节 w:字 2字节 b：单字节
#            movw $0x67fe,%ax # 立即数0x67fe存入ax中
#            movw %ax,%bx # ax的数存入bx中-
#            movb %bh,%cl # bx高8位67存入cx的低8位中
#            movb %bl,%ch # bx的低8位fe存入cx高8位中，cx的值:0xfe67
#            xchgb %cl,%ch #交换两个寄存器的值,cx的值:0x67fe
#            movl (msg),%eax #将变量中的内容放到寄存器中，从内存读始终是从低位开始读，寄存器右边低位左边高位，所以结果是：0x6c6c6568(lleh)
#            movb (msg),%al # 将内容存入al(ax低八位)中，将
#            movl (x),%eax # 值 0x12345678×（内存从低位开始读，先读78再读56...，寄存器右边低位左边高位,从右往作存78,56...）
#            movw (x),%dx # dx寄存器两个字节,结果为0x5678



#2、 加减指令
#            # inc指令：加1(一个操作数)
#            # dec指令: 减1(一个操作数)
#            movl $0xFFFFFFFF,%eax
#            movl $0x2D,%ebx
#            dec %ebx # 结果0x2C
#            inc %eax # 结果 0x0
#            movl $0x5,%eax # 设置eax的值为5
#     DoMore:dec %eax  # 减1 DoMore：是一个标号
#            jnz DoMore # 跳转到DoMore(如果eax的值不为0,当eax的值是0时，eflags寄存器的第6位（ZF，从右往左0开始），将会设置为1代表操作数为0了)
#                       # jnz指令每次循环都会判断ZF位
#


#3、 字符串转小写
#            movl $snippet,%ebx # 将字符串的地址(也是首字母的地址)放到ebx中
#            movl $0x8,%eax  # 循环次数8
#    DoMore2:addb $0x20,(%ebx) # 将地址中的字符（1个字节b）加32(十进制),变为小写字母
#            inc %ebx # 地址加1（指向下一个字符）
#            dec %eax # 减1 直到eax为0（循环8次）
#            jnz DoMore2 # 循环操作 最有snippet的所有字符变为小写
#            movl (snippet),%ebx # 将字符串(前4个字节)放到ebx中 结果 0x676e616b (gnak)


#4、负数补碼表示
#            movl $0x5,%eax  # 将0x5放入eax （有符号数：最高位（最左边）为1表示负数，为0表示正数）
#    DoMore3:dec %eax  # 递减
#            # jmp DoMore3  # 跳转 循环(注释掉，以免再次死循环) ,当eax减到-1时，为0xFFFFFFFF(负数的补碼。负数补碼：原码取反+1, 补码->原碼：减1取反)
#            #  cpu在晶体管逻辑层面上，并不真正需要减法，它只产生减数的补碼，并将其与被减数相加
#            #  如:3-2 = 3+(-2); -2以补碼的形式存放，直接按位相加即可。


#5、正数转负数
#            #neg指令 将一个数取负数（补碼形式）
#            movl $0xF,%eax  # 0xF 16
#            neg %eax  # 取负数 0xFFFFFFF1(-16的补碼)
#            addl $0xF,%eax  # 0

#6、带符号移动(复制)
#            movw $0xFFD6,%ax # -42放到ax(2个字节)中
#            movl %eax,%ebx # ebx（4字节） 还是 0x0000FFD6 (但表示的值是65494，原来的符号位变成了普通位)
#
#            # movsx指令：带符号一起移动
#            mov $0xFFD6,%ax
#            movsx %ax,%ebx # ebx (4字节) 0xFFFFFFD6(-42)
#

#7、 乘法
#            # mul:乘法(无符号) div：除法（无符号）
#            # imul:乘法(有符号) idiv：除法（有符号）
#            movl $447,%eax
#            movl $1739,%ebx
#            mul %ebx # 乘法，显式因子(r/m8(16,32) 任意8位，16位，32为的寄存器或内存地址，不能是立即数),隐式因子（al,ax,eax 根据显式因子的位数而定）
#                     # 结果存放在eax中 777333, 并且edx为0（edx用于保存乘法运算的部分结果）同时eflags寄存器的进位标志位CF（第0位）是0,表示无进位
#
#8、乘法 进位
#            # 将数设大一点
#            movl $0xFFFFFFFF,%eax
#            movl $0x3B72,%ebx
#            mul %ebx  # 计算结果 eax:0xffffc48e edx:0x3b71(进位后高位部分) eflags CF位设置位1(有进位)

#9、 除法
#           movl $0x8,%eax # 隐式操作数 被除数(商的整数部分 al,ax,eax 对应的余数部分ah,dx,edx)
#           movl $0x3,%ebx # 显式操作数 除数（r/m8(16,32) ）
#           div %ebx  # 结果 商为2保存在eax中，余数2保存在edx中

#10、push指令（将16位或32寄存器或内存值压入堆栈，64位linux编译不过，在代码顶部加上.code32） （其他push指令：pushf pushfd pusha pushad）
#            movl $0xaabbffee,%eax
#            pushl %eax # 将esp的值(栈顶元素的地址)减小4(堆栈增长方向从高到低,eax占4个字节),将eax寄存器中的数据压入堆栈(压入顺序:aa bb ff ee)
            #pushw %ax  # 将esp减小2（ax占2个字节）,将ax的值压栈
# pop指令（弹栈，弹出的字节数由操作数决定，只能是16位或32位）(其他pop指令:popa,popad,popf,popfd)
#           popw %bx # 取出栈顶的2个字节(bx的大小是2个字节)放到bx中 取出顺序 ee ff (存入bx结果: ffee ,从右往左存)


#11、系统调用 (控制台输出hello)
 #Linux的系统调用通过int 80h实现，用系统调用号来区分入口函数。 操作系统实现系统调用的基本过程是：
 #应用程序调用库函数（API）；
 #API将系统调用号存入EAX，然后通过中断调用使系统进入内核态；
 #内核中的中断处理函数根据系统调用号，调用对应的内核函数（系统调用）；
 #系统调用完成相应功能，将返回值存入EAX，返回到中断处理函数；
 #中断处理函数返回到API中；
 #API将EAX返回给应用程序。

           movl $len, %edx						#参数三 字符串长度 （edx）
           movl $msg, %ecx						#参数二 要显示的字符串(ecx)
           movl $1, %ebx						#参数一 文件描述符(1:标准输出 ebx)
           movl $4, %eax						#系统调用号(sys_write eax)
           int $0x80							#调用内核功能 int0x80指令首先把它后面的那条指令地址压入栈顶(记录系统调用后的返回地址)，系统调用完成后再用IRET指令弹出继续执行
           #退出程序
           movl $0, %ebx						#参数一 退出代码(ebx)
           movl $1, %eax						#系统调用号(sys_exit eax)
           int $0x80							#调用内核功能


